<!-- Last Updated: Friday May 31, 2002 Version 0.7-->
<html>
  <head>
    <title>SAX for Pascal Demo - Simple Parsing Demo</title>
  </head>
  <body>
    <div width="100%" align="right">
      <a href="http://xml.defined.net/SAX/">SAX for Pascal Home</a>
      &#160;
      <a href="http://xml.defined.net/SAX/docs">SAX for Pascal Documentation</a>
    </div>
    <h1>Simple parsing demo</h1>
    <p>This demo is designed to show the simplest demo of parsing
    available. It allows you to parse an XML file and report any errors
    you received. It will also fill a listbox with all of the element
    names it encounters. It demonstrates the use of an ErrorHandler and
    a ContentHandler. For a more complex demo please see the Filling
    a TreeView demo.
    </p>
    <h2>Adding the parsers to the project</h2>
    <p>In order to use a parser you must add the parser unit file to
    the your project. <i>(The technical reasons behind this are simple,
    the intialization code for each parser package must be called.
    By adding the unit to the project you can be sure that the
    project will execute the code in the package's initialization
    section)</i></p>
    <p>First click "<code>Project | Add To Project</code>" on the
    file menu. In this demo you should see that SAXKW
    has already been added for you. This allows you to use
    the default parser provided by Keith Wood. You
    can add another parser by including the unit that declares
    that package's vendor information. To see how to choose a
    parser from a list of available parsers, please see the demo
    "Choosing a parser from a list".</p>
    <h2>Widestrings?</h2>
    <p>Another important decision is whether or not you will be using
    WideStrings in your application. If you are using simple ASCII files
    you don't need to-- however, to support some Unicode encodings you
    need to use WideChar and WideStrings. Within SAX you can control this
    using a conditional directive in the Project Options. To use WideStrings, add
    SAX_WIDESTRINGS to the Conditional Defines. If you change the value for this,
    you may need to recompile (build) your entire project for the setting to
    take effect. <i>(Note: If you are using the
    <a href="http://xml.defined.net/SAX/expat">KDSSAXExpat</a> wrapper, you will also
    need to include the conditional define XML_WIDESTRINGS)</i></p>
    <h2>Setting up the form</h2>
    <p>For this demo we will need to add a few components to
    the form. First we will need a listbox for our output.
    Second we will need a button to start our parsing. We also
    want to add an OpenDialog so that we can search for the file.
    Finally we will need to add a <code>TSAXContentHandler</code>
    and a <code>TSAXErrorHandler</code>. The SAX components are installed
    to the SAX tab in the Component Palette by default.</p>
    <h2>Basic Events</h2>
    <p>Now that you have added all of the components let's set up some events.
    We will begin by adding event handlers to the <code>TSAXErrorHandler</code>.
    In each event we will simply add the message to the our Listbox1.</p>
    <p>
      <pre>
        procedure TForm1.SAXErrorHandler1Error(Sender: TObject;
          const Error: ISAXParseError);
        begin
          ListBox1.Items.Add('[Error] ' + Error.getMessage + ' Line ' +
            IntToStr(Error.getLineNumber) + ' Column ' + IntToStr(Error.getColumnNumber));
        end;

        procedure TForm1.SAXErrorHandler1FatalError(Sender: TObject;
          const Error: ISAXParseError);
        begin
          ListBox1.Items.Add('[Fatal Error] ' + Error.getMessage + ' Line ' +
            IntToStr(Error.getLineNumber) + ' Column ' + IntToStr(Error.getColumnNumber));
        end;

        procedure TForm1.SAXErrorHandler1Warning(Sender: TObject;
          const Error: ISAXParseError);
        begin
          ListBox1.Items.Add('[Warning] ' + Error.getMessage + ' Line ' +
            IntToStr(Error.getLineNumber) + ' Column ' + IntToStr(Error.getColumnNumber));
        end;
      </pre>
    </p>
    <p>That's all we need to report errors. Now we we add an OnStartElement event to our
    <code>TSAXContentHandler</code>. It will simply add the name of the element
    to our Listbox1.</p>
    <p>
      <pre>
        procedure TForm1.SAXContentHandler1StartElement(Sender: TObject;
          const NamespaceURI, LocalName, QName: WideString;
          const Atts: IAttributes);
        begin
          ListBox1.Items.Add('[Element] ' + QName);
        end;
      </pre>
    </p>
    <p> Notice that we aren't outputing the Line and Column information.
    The IErrorHandler interface provides the Line and Column information
    for us automatically. However, in order to get this information for other
    interfaces we need to use an ILocator. This information usually isn't necassary
    though (except when reporting errors). For more information on ILocator
    please see the demo "Using Locator Information".</p>
    <h2>Parsing the File</h2>
    <p>We've got the setup out of the way, now we are ready to parse an XML
    document. We'll put all of our parsing code in the OnClick event handler
    for Button1.</p>
    <p>We will begin by locating the file we want to parse. We can do this
    using our OpenDialog. Then we will need to obtain an XMLReader. Because
    we have already included a SAX Parser in our project, we can just get the
    default vendor and default XMLReader interface. Next, we'll need
    to tell the XMLReader interface about our ContentHandler and ErrorHandler
    so it will fire our events. After that, all we need to
    do is call the parse method.</p>
    <p>
      <pre>
        procedure TForm1.Button1Click(Sender: TObject);
        var XMLReader : IXMLReader;
        begin
          ListBox1.Items.Clear;
          if (OpenDialog1.Execute) then
          begin
            // Get the Default SAX Vendor and XML Reader
            XMLReader:= GetSAXVendor.XMLReader;
            XMLReader.setContentHandler(SAXContentHandler1);
            XMLReader.setErrorHandler(SAXErrorHandler1);
            XMLReader.parse(OpenDialog1.FileName);
            XMLReader:= nil;
          end;
        end;
      </pre>
    </p>
    <p>
      We could leave our code like this and everything would work great. Instead,
      let's add one more step: checking to see if our Vendor is a
      <code>TSAXBufferedVendor</code>. It is important to be aware that in SAX
      for Pascal there are two kinds of Vendors: Normal and Buffered. A
      <i>Buffered</i> Vendor is a vendor that is relying on the Buffered
      (or PSAXChar) versions of the interfaces, such as
      <code>IBufferedXMLReader</code>. Generally these parsers are much
      faster because they require fewer string allocations. A good example of
      a Buffered SAX vendor is <a href="http://xml.defined.net/SAX/expat">KDSSAXExpat</a>.
    </p>
    <p>
      We'll have to add the <code>BSAX</code> unit to our uses clause. In SAX
      for Pascal, all of the Buffered interfaces are in units that begin with
      a "B". In order to make use of the Buffered Vendor we'll use an adapter
      class, so we'll also need to use the unit <code>SAXAdapters</code>.
    </p>
    <p>
      <pre>
        procedure TForm1.Button1Click(Sender: TObject);
        var
          XMLReader : IXMLReader;
          XMLBufReader: IBufferedXMLReader;
          Vendor: TSAXVendor;
          ContentHandler: IContentHandler;
        begin
          ListBox1.Items.Clear;
          ContentHandler:= SAXContentHandler1;
          if (OpenDialog1.Execute) then
          begin
            // Get the Default SAX Vendor
            Vendor:= GetSAXVendor;
            if Vendor is TBufferedSAXVendor then
            begin
              XMLBufReader:= TBufferedSAXVendor(Vendor).BufferedXMLReader;
              XMLBufReader.setContentHandler(Adapt(ContentHandler, XMLBufReader));
              XMLBufReader.setErrorHandler(SAXErrorHandler1);
              XMLBufReader.parse(OpenDialog1.FileName);
              XMLBufReader:= nil;
            end else
            begin
              XMLReader:= Vendor.XMLReader;
              XMLReader.setContentHandler(ContentHandler);
              XMLReader.setErrorHandler(SAXErrorHandler1);
              XMLReader.parse(OpenDialog1.FileName);
              XMLReader:= nil;
            end;
          end;
        end;
      </pre>
    </p>
    <p>
      We grab a reference to the Default Vendor just as we did before. However,
      we check to see what kind of Vendor it is before using it. Based on the
      type we access the <code>XMLReader</code> or <code>BufferedXMLReader</code>
      property. As a final difference, we must use an adapter for our
      ContentHandler because the two interfaces use different string
      conventions. In the SAXAdapters unit, there are many pre-built adapters
      for converting the two kinds of interfaces back and forth. The
      <code>Adapt</code> function is overloaded to understand almost any kind
      of SAX interface you send to it (this excludes <code>IXMLReader</code> and
      <code>IBufferedXMLReader</code>).
    </p>
    <h2>What is the output?</h2>
    <p>In the Simple Parsing Demo folder there are two XML documents. The first
    document, <code>Document1.xml</code>, when parsed should have the following
    output:</p>
    <p>
      <pre>
        [Element] Document
        [Element] Element1
        [Element] Element2
        [Element] Element3
        [Element] Element4
        [Element] Element5
        [Element] Element6
        [Element] Element7
      </pre>
    </p>
    <p>The second document, <code>Document2.xml</code>, contains errors. However
    we have registered an ErrorHandler so we should see the Error messages reported
    in ListBox1. When parsed, you should have the following output: </p>
    <p>
      <pre>
        [Element] Document
        [Element] Element1
        [Element] Element2
        [Element] Element3
        [Fatal Error] Mismatched closing tag - Element4 - should be Document Line 7 Column 17
      </pre>
    </p>
  </body>
</html>

